// Copyright (c) 2020 Presto Labs Pte. Ltd.
// Author: taekwon

#pragma once

#include <memory>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <map>

#include <rapidjson/document.h>

#include "coin2/exchange/base/order/order.h"
#include "coin2/exchange/okex/symbology/currency.h"
#include "coin2/exchange/okex_futures/order_v3/new_fill_detector.h"
#include "coin2/exchange/okex_futures/symbology/product.h"

namespace coin2::exchange::okex_futures::order_v3 {

class OkexFuturesParser : public base::order::BaseParser {
 public:
  using TransferType = coin::proto::TransferRequestProto::TransferType;

 public:
  OkexFuturesParser() : is_prepared_(false) {}
  const ParseResult& ParseRawMessage(const RawMessage& msg) override;
  AccountBalance ParseBalance(const JsonValue& document) override;
  AccountPosition ParsePosition(const JsonValue& document) override;

 private:
  void ParseSubmitResponse();
  void ParseCancelResponse();
  void ParseRestOrdersInfo();
  void ParseRestFillInfoList();
  void ParseRestAccountBalance();
  void ParseRestAccountPosition();
  void ParseRestTransfer();
  void ParseWsMessage();

  AccountBalance ParseProductBalance(const JsonValue& doc);
  AccountBalance ParseSwapProductBalance(const JsonValue& doc);
  AccountPosition ParseProductPositionFromRest(const JsonValue& doc);
  AccountPosition ParseSwapProductPositionFromRest(const JsonValue& doc);
  AccountPosition ParsePositionFromWs(const JsonValue& doc);
  AccountPosition ParseSwapPositionFromWs(const JsonValue& doc);
  ProductFillBundle ParseRestFillInfoListV2(const JsonValue& data);
  ProductFillBundle ParseSwapRestFillInfoList(const JsonValue& data);
  ProductOrderBundle ParseWsOrderInfoListV2(const JsonValue& data);
  ProductFillBundle ParseWsFillInfoListV2(const JsonValue& data);
  ProductFillBundle ParseSwapWsFillInfoList(const JsonValue& data);
  void SetAccountBalanceProtoFromRaw(const JsonValue& data, AccountBalance* acc_balance);
  void SetAccountPositionProtoFromRaw(const JsonValue& data, AccountPosition* acc_position);
  void SetSwapAccountPositionProtoFromRaw(
      const JsonValue& data,
      const std::string& instrument_id,
      AccountPosition* acc_position);
  coin::proto::ProductOrderElement ParseOrderInfo(const JsonValue& data);
  AccountBalance UpdateUsdtBalance(const AccountBalance& acc_balance);
  void Prepare();

 private:
  std::unordered_set<std::string> usdt_ccys_;
  std::unordered_map<std::string, coin::proto::CurrencyBalance> usdt_bals_;
  bool ignore_balance_event_;
  bool is_prepared_;
  NewFillDetector new_fill_detector_;

  static std::map<std::string, TransferType> futures_transfer_type_map_;
  static std::map<std::string, TransferType> perpetual_transfer_type_map_;
};

}  // namespace coin2::exchange::okex_futures::order_v3
